/*
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;

using Gtk;
using Glade;
using Mono.Unix;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Managers;
using DCSharp.Backend.Objects;

namespace DCSharp.Gui
{
	public class HubWindow : GladeWindow
	{
		private enum Column
		{
			HubInfo,
			Online
		}

		private HubManager hubManager;
		private IFavoriteHubManager favoriteHubManager;

		private FavoriteHubInfo selectedHub;

		private ListStore listStore;
		private ListComparer comparer;

		[Widget]
		private Button closeButton;

		[Widget]
		private TreeView hubTreeView;

		[Widget]
		private VBox infoBox;

		[Widget]
		private Entry nameEntry;

		[Widget]
		private Entry addressEntry;

		[Widget]
		private Entry descriptionEntry;

		[Widget]
		private CheckButton identificationNeededButton;

		[Widget]
		private Table identificationTable;

		[Widget]
		private Entry usernameEntry;

		[Widget]
		private Entry passwordEntry;

		[Widget]
		private CheckButton autoConnectCheckButton;

		[Widget]
		private Button addHubButton;

		[Widget]
		private Button removeHubButton;

		[Widget]
		private Button connectButton;

		#region Constructor

		public HubWindow(HubManager hubManager, IFavoriteHubManager favoriteHubManager,
			Window parent) : base("HubWindow.glade", parent)
		{
			if (hubManager == null)
			{
				throw new ArgumentNullException("hubManager");
			}
			if (favoriteHubManager == null)
			{
				throw new ArgumentNullException("favoriteHubManager");
			}
			this.hubManager = hubManager;
			this.favoriteHubManager = favoriteHubManager;

			infoBox.Sensitive = false;
			removeHubButton.Sensitive = false;
			connectButton.Sensitive = false;
			identificationTable.Sensitive = identificationNeededButton.Active;

			// Hub list
			listStore = new ListStore(typeof(FavoriteHubInfo), typeof(bool));

			hubTreeView.Model = listStore;
			hubTreeView.Selection.Changed += OnSelectionChanged;
			hubTreeView.RowActivated += OnRowActivated;

			TreeViewColumn column = new TreeViewColumn();
			column.Title = Catalog.GetString("Name");
			column.Spacing = 3;
			hubTreeView.AppendColumn(column);

			CellRendererPixbuf pixbufRenderer = new CellRendererPixbuf();
			column.PackStart(pixbufRenderer , false);
			column.SetCellDataFunc(pixbufRenderer, SetPixbuf);

			CellRendererText textRenderer = new CellRendererText();
			textRenderer.Ellipsize = Pango.EllipsizeMode.End;
			column.PackStart(textRenderer , true);
			column.SetCellDataFunc(textRenderer, SetName);

			// Events
			addHubButton.Clicked += OnAddHubButtonClicked;
			removeHubButton.Clicked += OnRemoveHubButtonClicked;

			nameEntry.FocusOutEvent += delegate
			{
				if (SelectedHub != null && nameEntry.Text.Length > 0)
				{
					SelectedHub.Name = nameEntry.Text;
					hubTreeView.QueueDraw();
				}
				else if (SelectedHub != null)
				{
					nameEntry.Text = SelectedHub.Name;
				}
			};
			addressEntry.Changed += delegate
			{
				UpdateSensitivity();
				UpdateSelectedHubStatus();
			};
			addressEntry.FocusOutEvent += delegate
			{
				if (SelectedHub != null && addressEntry.Text.Length > 0)
					SelectedHub.Hostname = addressEntry.Text;
				else if (SelectedHub != null)
					addressEntry.Text = SelectedHub.Hostname;
			};
			descriptionEntry.FocusOutEvent += delegate
			{
				if (SelectedHub != null)
					SelectedHub.Description = descriptionEntry.Text;
			};
			usernameEntry.FocusOutEvent += delegate
			{
				if (SelectedHub != null)
					SelectedHub.Nick = usernameEntry.Text;
			};
			passwordEntry.FocusOutEvent += delegate
			{
				if (SelectedHub != null)
					SelectedHub.Password = passwordEntry.Text;
			};
			autoConnectCheckButton.Toggled += delegate
			{
				if (SelectedHub != null)
					SelectedHub.AutoConnect = autoConnectCheckButton.Active;
			};

			identificationNeededButton.Toggled += delegate
			{
				if (SelectedHub != null)
				{
					identificationTable.Sensitive = identificationNeededButton.Active;

					if (identificationNeededButton.Active)
					{
						SelectedHub.Nick = usernameEntry.Text;
						SelectedHub.Password = passwordEntry.Text;
					}
					else
					{
						SelectedHub.Nick = "";
						SelectedHub.Password = "";
					}
				}
			};

			// Sort the hubs when the list is reordered
			comparer = new ListComparer(listStore);
			listStore.RowDeleted += delegate
			{
				favoriteHubManager.Sort(comparer);
			};

			// Events
			connectButton.Clicked += OnConnectButtonClicked;
			closeButton.Clicked += OnCloseButtonClicked;

			hubManager.HubConnected += OnHubStateChanged;
			hubManager.HubDisconnected += OnHubStateChanged;

			// Initialize
			lock (favoriteHubManager.SyncRoot)
			{
				HubConnection hub;
				foreach (FavoriteHubInfo hubInfo in favoriteHubManager.Hubs)
				{
					listStore.AppendValues(hubInfo,
						hubManager.TryGetHub(hubInfo.Hostname, out hub));
				}
				favoriteHubManager.HubAdded += OnFavoriteHubAdded;
				favoriteHubManager.HubRemoved += OnFavoriteHubRemoved;
			}
		}

		#endregion

		#region Classes

		private class ListComparer : IComparer<FavoriteHubInfo>
		{
			private ListStore listStore;

			public ListComparer(ListStore listStore)
			{
				this.listStore = listStore;
			}

			public int Compare(FavoriteHubInfo a, FavoriteHubInfo b)
			{
				if (a == b)
				{
					return 0;
				}

				TreeIter iter;
				if (listStore.GetIterFirst(out iter))
				{
					int row = 0, rowA = 0, rowB = 0;
					do
					{
						object obj = listStore.GetValue(iter, (int)Column.HubInfo);
						if (obj == a)
						{
							rowA = row;
						}
						else if (obj == b)
						{
							rowB = row;
						}
						row++;
					}
					while (listStore.IterNext(ref iter));

					return rowA.CompareTo(rowB);
				}
				return 0;
			}
		}

		#endregion

		#region Properties

		private FavoriteHubInfo SelectedHub
		{
			get { return selectedHub; }
		}

		#endregion

		#region Methods

		public override void Destroy()
		{
			hubManager.HubConnected -= OnHubStateChanged;
			hubManager.HubDisconnected -= OnHubStateChanged;

			favoriteHubManager.HubAdded -= OnFavoriteHubAdded;
			favoriteHubManager.HubRemoved -= OnFavoriteHubRemoved;

			base.Destroy();
		}

		private void Load(FavoriteHubInfo hubInfo)
		{
			removeHubButton.Sensitive = hubInfo != null;
			connectButton.Sensitive = hubInfo != null;
			infoBox.Sensitive = hubInfo != null;

			if (hubInfo != null)
			{
				nameEntry.Text = hubInfo.Name ?? Catalog.GetString("Hub");
				addressEntry.Text = hubInfo.Hostname ?? Catalog.GetString("Address");
				descriptionEntry.Text = hubInfo.Description ?? String.Empty;
				usernameEntry.Text = hubInfo.Nick ?? String.Empty;
				passwordEntry.Text = hubInfo.Password ?? String.Empty;

				autoConnectCheckButton.Active = hubInfo.AutoConnect;
				identificationNeededButton.Active = !(String.IsNullOrEmpty(hubInfo.Nick) &&
					String.IsNullOrEmpty(hubInfo.Password));
			}
			else
			{
				nameEntry.Text = addressEntry.Text = descriptionEntry.Text = String.Empty;
				usernameEntry.Text = passwordEntry.Text = String.Empty;
				autoConnectCheckButton.Active = false;
			}
		}

		private void UpdateSensitivity()
		{
			Uri uri;
			bool valid = StringUtil.TryCreateUri(addressEntry.Text, out uri);

			connectButton.Sensitive = valid;
			if (!valid)
			{
				Gdk.Color background  = new Gdk.Color(193, 102, 90);
				addressEntry.ModifyBase(StateType.Normal, background);
			}
			else
			{
				addressEntry.ModifyBase(StateType.Normal);
			}
		}

		private void UpdateSelectedHubStatus()
		{
			TreeIter iter;
			if (hubTreeView.Selection.GetSelected(out iter))
			{
				HubConnection connection;
				hubManager.TryGetHub(addressEntry.Text, out connection);

				bool online = connection != null &&
					connection.State == ConnectionState.Connected;

				listStore.SetValue(iter, (int)Column.Online, online);
			}
			UpdateConnectButton();
		}

		private void UpdateConnectButton()
		{
			HubConnection connection;
			hubManager.TryGetHub(addressEntry.Text, out connection);

			bool disconnected = connection == null || (connection != null &&
				connection.State == ConnectionState.Disconnected);

			connectButton.Label = disconnected ? Stock.Connect : Stock.Disconnect;
		}

		private void SetPixbuf(TreeViewColumn column, CellRenderer renderer,
			TreeModel model, TreeIter iter)
		{
			CellRendererPixbuf pixbufRenderer = (CellRendererPixbuf)renderer;

			bool online = (bool)model.GetValue(iter, (int)Column.Online);

			pixbufRenderer.Pixbuf = Util.GetHubIcon(online);
		}

		private void SetName(TreeViewColumn column, CellRenderer renderer,
			TreeModel model, TreeIter iter)
		{
			CellRendererText textRenderer = (CellRendererText)renderer;
			FavoriteHubInfo hubInfo = (FavoriteHubInfo)model.GetValue(iter,
				(int)Column.HubInfo);

			textRenderer.Text = hubInfo.Name;
		}

		private void OnSelectionChanged(object obj, EventArgs args)
		{
			TreeIter iter;
			if (hubTreeView.Selection.GetSelected(out iter))
			{
				selectedHub = (FavoriteHubInfo)listStore.GetValue(iter,
					(int)Column.HubInfo);
			}
			else
			{
				selectedHub = null;
			}
			Load(selectedHub);
		}

		private void OnRowActivated(object obj, RowActivatedArgs args)
		{
			TreeIter iter;
			if (listStore.GetIter(out iter, args.Path))
			{
				FavoriteHubInfo hubInfo = (FavoriteHubInfo)listStore.GetValue(iter, 0);

				if (connectButton.Sensitive)
				{
					HubConnection hub = Globals.Runtime.ConnectToHub(hubInfo);
					Globals.Gui.ShowHub(hub, true);
				}
			}
		}

		private void OnAddHubButtonClicked(object obj, EventArgs args)
		{
			FavoriteHubInfo hubInfo = new FavoriteHubInfo(
				Catalog.GetString("Hub"), Catalog.GetString("Address"));

			favoriteHubManager.Add(hubInfo);
		}

		private void OnRemoveHubButtonClicked(object obj, EventArgs args)
		{
			TreeIter iter;
			if (hubTreeView.Selection.GetSelected(out iter))
			{
				FavoriteHubInfo hubInfo = (FavoriteHubInfo)listStore.GetValue(iter, 0);
				favoriteHubManager.Remove(hubInfo);
			}
		}

		private void OnConnectButtonClicked(object obj, EventArgs args)
		{
			if (SelectedHub != null)
			{
				HubConnection connection;
				hubManager.TryGetHub(addressEntry.Text, out connection);

				if (connection != null &&
					connection.State != ConnectionState.Disconnected)
				{
					connection.Disconnect();
				}
				else if (connectButton.Sensitive)
				{
					Globals.Runtime.ConnectToHub(SelectedHub);
				}
			}
		}

		private void OnCloseButtonClicked(object obj, EventArgs args)
		{
			Destroy();
		}

		private void OnHubStateChanged(object obj, ConnectionEventArgs args)
		{
			HubConnection connection = (HubConnection)args.Connection;
			bool online = connection.State == ConnectionState.Connected;

			Application.Invoke(delegate
			{
				FavoriteHubInfo hubInfo = null;
				foreach (FavoriteHubInfo info in favoriteHubManager.Hubs)
				{
					Uri uri;
					if (info.Hostname != null &&
						StringUtil.TryCreateUri(info.Hostname, out uri) &&
						uri == connection.Uri)
					{
						hubInfo = info;
						break;
					}
				}

				TreeIter iter;
				if (hubInfo != null && this.GetHubIter(hubInfo, out iter))
				{
					listStore.SetValue(iter, (int)Column.Online, online);
				}
				UpdateConnectButton();
			});
		}

		private void OnFavoriteHubAdded(object obj, FavoriteHubEventArgs args)
		{
			Application.Invoke(delegate
			{
				TreeIter iter = listStore.AppendValues(args.FavoriteHubInfo);
				hubTreeView.Selection.SelectIter(iter);

				nameEntry.GrabFocus();
			});
		}

		private void OnFavoriteHubRemoved(object obj, FavoriteHubEventArgs args)
		{
			Application.Invoke(delegate
			{
				TreeIter iter;
				if (!GetHubIter(args.FavoriteHubInfo, out iter))
				{
					return;
				}

				if (args.FavoriteHubInfo == SelectedHub)
				{
					TreePath path = listStore.GetPath(iter);
					int row = path.Indices[0];

					listStore.Remove(ref iter);

					// Select the next or previous row
					int rows = hubTreeView.Model.IterNChildren();
					if (row >= rows)
					{
						row = rows - 1;
					}
					if (row >= 0 && listStore.IterNthChild(out iter, row))
					{
						hubTreeView.Selection.SelectIter(iter);
					}
				}
				else
				{
					listStore.Remove(ref iter);
				}
			});
		}

		private bool GetHubIter(FavoriteHubInfo hubInfo, out TreeIter retIter)
		{
			TreeIter iter;
			if (listStore.GetIterFirst(out iter))
			{
				do
				{
					FavoriteHubInfo info = (FavoriteHubInfo)listStore.GetValue(iter,
						(int)Column.HubInfo);

					if (info == hubInfo)
					{
						retIter = iter;
						return true;
					}
				}
				while (listStore.IterNext(ref iter));
			}
			retIter = TreeIter.Zero;
			return false;
		}

		#endregion
	}
}
